package com.titanrise.crm.common.utils;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

public class Functions {
    public static <A> Function<A, A> forEach(Consumer<A> consumer) {
        return (a) -> {
            consumer.accept(a);
            return a;
        };
    }

    public static <A> Function<A, Nothing> ofConsumer(Consumer<A> consumer) {
        return (a) -> {
            consumer.accept(a);
            return Nothing.value;
        };
    }

    public static <A, B, C> Function<Pair<A, B>, C> ofBiFunction(BiFunction<A, B, C> biFunction) {
        return (pair) -> biFunction.apply(pair.getFirst(), pair.getSecond());
    }

    public static <A, B, C, D> Function<Triple<A, B, C>, D> ofTriFunction(TriFunction<A, B, C, D> triFunction) {
        return (trip) -> triFunction.apply(trip.getFirst(), trip.getSecond(), trip.getThird());
    }

    public static <A> A identity(A a) {
        return a;
    }

    public static <A, B> Function<A, B> flow(Function<A, B> fn1) {
        return fn1;
    }

    public static <A, B, C> Function<A, C> flow(Function<A, B> fn1, Function<B, C> fn2) {
        return fn1.andThen(fn2);
    }

    public static <A, B, C, D> Function<A, D> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3) {
        return fn1.andThen(fn2).andThen(fn3);
    }

    public static <A, B, C, D, E> Function<A, E> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4);
    }

    public static <A, B, C, D, E, F> Function<A, F> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5);
    }

    public static <A, B, C, D, E, F, G> Function<A, G> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6);
    }

    public static <A, B, C, D, E, F, G, H> Function<A, H> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7);
    }


    public static <A, B, C, D, E, F, G, H, I> Function<A, I> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7, Function<H, I> fn8) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).andThen(fn8);
    }

    public static <A, B, C, D, E, F, G, H, I, J> Function<A, J> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7, Function<H, I> fn8, Function<I, J> fn9) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).andThen(fn8).andThen(fn9);
    }

    public static <A, B, C, D, E, F, G, H, I, J, K> Function<A, K> flow(Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7, Function<H, I> fn8, Function<I, J> fn9, Function<J, K> fn10) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).andThen(fn8).andThen(fn9).andThen(fn10);
    }

    public static <A> A pipe(A a) {
        return a;
    }

    public static <A, B> B pipe(A a, Function<A, B> fn1) {
        return fn1.apply(a);
    }

    public static <A, B, C> C pipe(A a, Function<A, B> fn1, Function<B, C> fn2) {
        return fn1.andThen(fn2).apply(a);
    }

    public static <A, B, C, D> D pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3) {
        return fn1.andThen(fn2).andThen(fn3).apply(a);
    }

    public static <A, B, C, D, E> E pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).apply(a);
    }

    public static <A, B, C, D, E, F> F pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).apply(a);
    }

    public static <A, B, C, D, E, F, G> G pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).apply(a);
    }

    public static <A, B, C, D, E, F, G, H> H pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).apply(a);
    }

    public static <A, B, C, D, E, F, G, H, I> I pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7, Function<H, I> fn8) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).andThen(fn8).apply(a);
    }

    public static <A, B, C, D, E, F, G, H, I, J> J pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7, Function<H, I> fn8, Function<I, J> fn9) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).andThen(fn8).andThen(fn9).apply(a);
    }

    public static <A, B, C, D, E, F, G, H, I, J, K> K pipe(A a, Function<A, B> fn1, Function<B, C> fn2, Function<C, D> fn3, Function<D, E> fn4, Function<E, F> fn5, Function<F, G> fn6, Function<G, H> fn7, Function<H, I> fn8, Function<I, J> fn9, Function<J, K> fn10) {
        return fn1.andThen(fn2).andThen(fn3).andThen(fn4).andThen(fn5).andThen(fn6).andThen(fn7).andThen(fn8).andThen(fn9).andThen(fn10).apply(a);
    }
}
